#!/usr/bin/python
#!encoding: utf-8

import os, sys, shutil, yaml, getopt, socket, pty, subprocess
import urllib
from flask import Flask
from flask import request
from flask import render_template
from flask import jsonify

## apscheduler不同的版本支持不同的python, 所以需要根据情况进行导入操作
import apscheduler
if apscheduler.version == '1.3.1' or apscheduler.version == '2.1.2':
    from apscheduler.scheduler import Scheduler
elif apscheduler.version == '3.3.1':
    from apscheduler.schedulers.background import BackgroundScheduler

from prochandler import ProcHandler
from utils import makeJsonMsg, get_version, is_port_open
from validator import Validator
from daemonit import Daemonit

## 这是命令行可指定的配置选项默认值, 如果命令行中有指定, 可以直接替换
cmd_cfg = {
    'config_file'   : '/etc/pyadmin.yml',
    'debug':        False
}
app = Flask(__name__)
proc_handler = None
daemonit = None
cfg_opts = None

#######################################################################
@app.route('/pyadmin/reload')
def reloadCfg():
    proc_handler.reloadCfg(reload_cfg())
    return 'success'

@app.route('/pyadmin/shutdown')
def shutdown():
    try:
        global daemonit 
        daemonit.stop()
    except Exception, e:
        return 'failed'

@app.route('/pyadmin/upgrade')
def upgrade():
    tmpPath = '/tmp/.pyupgrade'
    pkg_url = request.args.get('pkgaddr', '')[2:-2]
    pkg_name = os.path.basename(pkg_url)
    if pkg_url == '' or pkg_url == None:
        return 'failed'

    if os.path.exists(tmpPath):
        if os.path.isfile(tmpPath):
            try:
                os.remove(tmpPath)
            except OSError, e:
                print(e)
                return 'failed'
        if os.path.isdir(tmpPath):
            try:
                ## removedirs删除非空目录不好使
                ## os.removedirs(tmpPath)
                shutil.rmtree(tmpPath)
            except OSError, e:
                print(e)
                return 'failed'
    
    os.makedirs(tmpPath)

    ## 下载文件
    urllib.urlretrieve(pkg_url, '%s/%s' % (tmpPath, pkg_name))
    result = os.popen('pip uninstall -y PYAdmin && pip install %s/%s' % (tmpPath, pkg_name)).read()
    return result

@app.route('/process/start')
def startProc():
    namelist = request.args.get('namelist', '').split(',')
    resultList = proc_handler.startProc(namelist)
    return makeJsonMsg(resultList)

@app.route('/process/stop')
def stopProc():
    namelist = request.args.get('namelist', '').split(',')
    resultList = proc_handler.stopProc(namelist)
    return makeJsonMsg(resultList)

@app.route('/process/kill')
def killProc():
    namelist = request.args.get('namelist', '').split(',')
    resultList = proc_handler.killProc(namelist)
    return makeJsonMsg(resultList)

@app.route('/process/status')
def getProcStatus():
    nameList = request.args.get('namelist', '').split(',')
    resultList = proc_handler.getProcStatus(nameList)
    return makeJsonMsg(resultList)

@app.route('/process/mkcd')
def coolDownProc():
    namelist = request.args.get('namelist', '').split(',')
    resultList = proc_handler.coolDownProc(namelist)
    return makeJsonMsg(resultList)

@app.route('/process/rmcd')
def meltUpProc():
    namelist = request.args.get('namelist', '').split(',')
    resultList = proc_handler.meltUpProc(namelist)
    return makeJsonMsg(resultList)

@app.route('/process/showcd')
def getCdListStr():
    return proc_handler.getCdListStr()

## apscheduler模块的jobs参数必须是最外层的函数, 不支持类对象的成员方法
def healthCheck():
    proc_handler.updateProcStatus()

def usage():
    pass

def validate_opts(opts):
    for key, val in opts:
        ## help, version的选项优先级最高
        if key in ['--help']:
            usage()
            sys.exit()
        if key in ['-v', '--version']:
            print(get_version())
            sys.exit()
        if key in ['-d', '--debug']:
            cmd_cfg['debug'] = True
        if key in ['-c', '--config']:
            if os.path.isfile(val):
                cmd_cfg['config_file'] = val
            else:
                print('Invalid config file path')
                sys.exit()

def load_cfg():
    global cfg_opts
    try:
        cfg_file = file(cmd_cfg['config_file'])
        cfg_opts = yaml.load(cfg_file)
        cfg_file.close()
    except Exception, e:
        print(e)
        sys.exit()

    cfg_opts = Validator.validate_main(cfg_opts)

    if cfg_opts == None:
        print('Error: invalid config syntax')
        sys.exit()

    ## 如果配置文件中是True, 就不用管命令行了, 反正命令行也只能设置为True
    if not cfg_opts['host']['pyadmin']['debug']:
        cfg_opts['host']['pyadmin']['debug'] = cmd_cfg['debug']

    return cfg_opts

def reload_cfg():
    cfg_opts = load_cfg()
    cfg4Handler = (
        cfg_opts['host']['procs'], 
        cfg_opts['host']['groups']
    )
    return cfg4Handler

def worker(cfg_opts):
    ## 健康检查间隔时间, 单位为秒
    interval = 3
    if apscheduler.version == '1.3.1' or apscheduler.version == '2.1.2':
        from apscheduler.scheduler import Scheduler
        scheduler = Scheduler()
        scheduler.add_interval_job(healthCheck, seconds = interval)
    elif apscheduler.version == '3.3.1':
        from apscheduler.schedulers.background import BackgroundScheduler
        scheduler = BackgroundScheduler()
        # 间隔3秒钟执行一次健康检查
        # 调度任务是一个独立的线程
        scheduler.add_job(healthCheck, 'interval', seconds = interval)

    scheduler.start()

    ## 启动flask进程
    app.run(
        host = cfg_opts['host']['pyadmin']['ip'],
        port = cfg_opts['host']['pyadmin']['port'],
        threaded=True
    )

def main():
    ## 解析命令行选项
    try:
        opts, args = getopt.getopt(
            sys.argv[1:],
            'hvdc:',
            ['help', 'version', 'debug', 'config=']
        )
    except getopt.GetoptError:
        sys.exit()

    ## 验证命令行选项
    validate_opts(opts)
    cfg_opts = load_cfg()

    global proc_handler
    proc_handler = ProcHandler(
        (cfg_opts['host']['procs'], 
        cfg_opts['host']['groups'])
    )
    global daemonit
    daemonit = Daemonit(
        pidfile = cfg_opts['host']['pyadmin']['pid_file'],
        stdout_logfile = cfg_opts['host']['pyadmin']['log_file'],
        stderr_logfile = cfg_opts['host']['pyadmin']['log_file']
    )

    ## 检测端口是否被占用
    if is_port_open(cfg_opts['host']['pyadmin']['ip'], cfg_opts['host']['pyadmin']['port']):
        print('目标端口被占用 %s:%s' % (cfg_opts['host']['pyadmin']['ip'], cfg_opts['host']['pyadmin']['port']))
        sys.exit(1)

    if not cfg_opts['host']['pyadmin']['debug']:
        daemonit.start(worker, cfg_opts)
        print('daemonit completed...')
    else:
        worker(cfg_opts)

if __name__ == '__main__':
    main()